 package com.kulian.Signalling.system.service.impl.socket;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.kulian.Signalling.system.mode.event.DictEvent;
import com.kulian.Signalling.system.service.impl.socket.*;
import com.kulian.Signalling.system.service.impl.socket.handler.*;
import com.kulian.Signalling.system.mode.socket.SignalingMessage;
import com.kulian.Signalling.system.service.impl.socket.handler.MessageHandler;
import com.kulian.Signalling.system.service.WebSocketService;
import com.kulian.Signalling.system.service.CallControlService;
import jakarta.websocket.OnMessage;
import jakarta.websocket.Session;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * WebSocket服务实现类
 * 统一处理WebSocket消息发送和用户在线状态管理
 *
 * @author Ray.Hao
 * @since 3.0.0
 */
@Service
@Slf4j
public class WebSocketServiceImpl implements WebSocketService, MessageHandlerRegistry, CallControlService {

    // 在线用户映射表，key为用户名，value为用户在线信息
    private final Map<String, UserOnlineInfo> onlineUsers = new ConcurrentHashMap<>();
    
    private final ObjectMapper objectMapper;
    private final MessageSender messageSender;
    private final MessageHandlerFactory messageHandlerFactory;
    private final Map<String, MessageHandler> customHandlers = new ConcurrentHashMap<>();
    private final Map<String, CallSession> activeCalls = new ConcurrentHashMap<>();

    @Autowired
    public WebSocketServiceImpl(ObjectMapper objectMapper, 
                              MessageSender messageSender,
                              MessageHandlerFactory messageHandlerFactory) {
        this.objectMapper = objectMapper;
        this.messageSender = messageSender;
        this.messageHandlerFactory = messageHandlerFactory;
    }

    //==================================
    //      用户在线状态管理功能
    //==================================

    /**
     * 用户上线
     *
     * @param username  用户名
     * @param sessionId WebSocket会话ID（可选）
     */
    @Override
    public void userConnected(String username, String sessionId) {
        // 生成会话ID（如果未提供）
        String actualSessionId = sessionId != null ? sessionId : "session-" + System.nanoTime();
        UserOnlineInfo info = new UserOnlineInfo(username, actualSessionId, System.currentTimeMillis());
        onlineUsers.put(username, info);
        log.info("用户[{}]上线，当前在线用户数：{}", username, onlineUsers.size());
        
        // 通知在线用户状态变更
        notifyOnlineUsersChangeInternal();
    }
    @OnMessage
    public void onMessage(String message, Session session) {
        log.debug("Received WebSocket message: {}", message);
        handleMessage(message, session);
    }
    // 处理信令消息
    private void handleMessage(String message, Session session) {
        if (message == null || message.isEmpty()) {
            log.warn("Received empty message");
            return;
        }

        try {
            SignalingMessage signalingMessage = objectMapper.readValue(message, SignalingMessage.class);
            if (signalingMessage.getType() == null) {
                messageSender.sendError(session.getUserPrincipal().getName(), "Message type cannot be null");
                return;
            }

            String username = session.getUserPrincipal().getName();
            if (!onlineUsers.containsKey(username)) {
                messageSender.sendError(username, "User not logged in");
                return;
            }

            // 先检查是否有自定义处理器
            MessageHandler customHandler = customHandlers.get(signalingMessage.getType());
            if (customHandler != null) {
                try {
                    customHandler.handleMessage(signalingMessage, username);
                } catch (Exception e) {
                    log.error("Custom handler failed to process message", e);
                    messageSender.sendError(username, "Message processing failed");
                }
            } else {
                // 没有自定义处理器则使用工厂
                try {
                    messageHandlerFactory.handleMessage(signalingMessage, username);
                } catch (IllegalArgumentException e) {
                    log.error("Invalid message type: {}", signalingMessage.getType());
                    messageSender.sendError(username, "Invalid message type");
                } catch (Exception e) {
                    log.error("Failed to process message", e);
                    messageSender.sendError(username, "Message processing failed");
                }
            }
        } catch (JsonProcessingException e) {
            log.error("Message parsing failed", e);
            messageSender.sendError(session.getUserPrincipal().getName(), "Invalid message format");
        }
    }



    /**
     * 速率限制检查
     * @param username 用户名
     * @return 是否超过速率限制
     */
    private boolean isRateLimited(String username) {
        // TODO: 实现基于Redis的速率限制
        return false;
    }
    /**
     * 用户下线
     *
     * @param username 用户名
     */
    @Override
    public void userDisconnected(String username) {
        onlineUsers.remove(username);
        log.info("用户[{}]下线，当前在线用户数：{}", username, onlineUsers.size());
        
        // 通知在线用户状态变更
        notifyOnlineUsersChangeInternal();
    }

    /**
     * 获取在线用户列表
     *
     * @return 在线用户名列表
     */
    public List<UserOnlineDTO> getOnlineUsers() {
        return onlineUsers.values().stream()
                .map(info -> new UserOnlineDTO(info.getUsername(), info.getLoginTime()))
                .collect(Collectors.toList());
    }

    /**
     * 获取在线用户数量
     *
     * @return 在线用户数
     */
    public int getOnlineUserCount() {
        return onlineUsers.size();
    }

    /**
     * 检查用户是否在线
     *
     * @param username 用户名
     * @return 是否在线
     */
    public boolean isUserOnline(String username) {
        return onlineUsers.containsKey(username);
    }
    
    /**
     * 手动触发在线用户变更通知
     * 供外部手动触发通知使用
     */
    public void notifyOnlineUsersChange() {
        log.info("手动触发在线用户变更通知，当前在线用户数：{}", onlineUsers.size());
        notifyOnlineUsersChangeInternal();
    }
    
    /**
     * 内部通用通知方法
     * 通知所有客户端在线用户变更
     */
    private void notifyOnlineUsersChangeInternal() {
        try {
            OnlineUsersChangeEvent event = new OnlineUsersChangeEvent();
            event.setType("ONLINE_USERS_CHANGE");
            event.setCount(onlineUsers.size());
            event.setUsers(getOnlineUsers());
            event.setTimestamp(System.currentTimeMillis());
            
            messageSender.broadcast("/topic/online-users", event);
            log.debug("Sent online users change notification");
        } catch (Exception e) {
            log.error("Failed to send online users change event", e);
        }
    }

    /**
     * 用户在线信息
     */
    @Data
    private static class UserOnlineInfo {
        private final String username;
        private final String sessionId;
        private final long loginTime;
    }

    /**
     * 用户在线DTO（用于返回给前端）
     */
    @Data
    public static class UserOnlineDTO {
        private final String username;
        private final long loginTime;
    }

    /**
     * 在线用户变更事件
     */
    @Data
    private static class OnlineUsersChangeEvent {
        private String type;
        private int count;
        private List<UserOnlineDTO> users;
        private long timestamp;
    }

    //==================================
    //      消息处理器注册接口实现
    //==================================

    @Override
    public void registerHandler(String messageType, MessageHandler handler) {
        if (messageType == null || handler == null) {
            throw new IllegalArgumentException("Message type and handler cannot be null");
        }
        customHandlers.put(messageType, handler);
        log.info("Registered custom handler for message type: {}", messageType);
    }

    @Override
    public void unregisterHandler(String messageType) {
        if (messageType != null) {
            customHandlers.remove(messageType);
            log.info("Unregistered handler for message type: {}", messageType);
        }
    }

    @Override
    public boolean hasHandler(String messageType) {
        return messageType != null && customHandlers.containsKey(messageType);
    }

    //==================================
    //      呼叫控制接口实现
    //==================================

    @Override
    public void initiateCall(String caller, String callee) {
        if (!isUserOnline(callee)) {
            messageSender.sendError(caller, "User " + callee + " is offline");
            return;
        }

        String callId = "call-" + System.currentTimeMillis() + "-" + caller.hashCode();
        CallSession session = new CallSession(callId, caller, callee);
        activeCalls.put(callId, session);

        CallRequest request = new CallRequest(caller, System.currentTimeMillis());
        request.setCallId(callId);

        messageSender.sendToUser(callee, "/queue/call", request);
        log.info("Call initiated from {} to {}, callId: {}", caller, callee, callId);
    }

    @Override
    public void acceptCall(String callee, String caller) {
        String callId = findCallId(caller, callee);
        if (callId == null) {
            messageSender.sendError(callee, "No active call found");
            return;
        }

        CallSession session = activeCalls.get(callId);
        if (session != null) {
            session.setStatus("ACTIVE");
            CallResponse response = new CallResponse(callId, "ACCEPTED", "Call accepted");
            messageSender.sendToUser(caller, "/queue/call", response);
            log.info("Call accepted by {}, callId: {}", callee, callId);
        }
    }

    @Override
    public void rejectCall(String callee, String caller, String reason) {
        String callId = findCallId(caller, callee);
        if (callId == null) {
            messageSender.sendError(callee, "No active call found");
            return;
        }

        CallSession session = activeCalls.get(callId);
        if (session != null) {
            session.setStatus("ENDED");
            session.setEndTime(System.currentTimeMillis());
            CallResponse response = new CallResponse(callId, "REJECTED", reason);
            messageSender.sendToUser(caller, "/queue/call", response);
            activeCalls.remove(callId);
            log.info("Call rejected by {}, reason: {}, callId: {}", callee, reason, callId);
        }
    }

    @Override
    public void endCall(String userId, String callId) {
        CallSession session = activeCalls.get(callId);
        if (session != null) {
            session.setStatus("ENDED");
            session.setEndTime(System.currentTimeMillis());
            CallResponse response = new CallResponse(callId, "ENDED", "Call ended");
            String otherUser = session.getCaller().equals(userId) ? session.getCallee() : session.getCaller();
            messageSender.sendToUser(otherUser, "/queue/call", response);
            activeCalls.remove(callId);
            log.info("Call ended by {}, callId: {}", userId, callId);
        }
    }

    @Override
    public void sendMediaOffer(String userId, String callId, String sdpOffer) {
        CallSession session = activeCalls.get(callId);
        if (session != null && session.getStatus().equals("ACTIVE")) {
            String otherUser = session.getCaller().equals(userId) ? session.getCallee() : session.getCaller();
            MediaOffer offer = new MediaOffer(callId, userId, sdpOffer);
            messageSender.sendToUser(otherUser, "/queue/media", offer);
            log.debug("Media offer sent from {} to {}, callId: {}", userId, otherUser, callId);
        }
    }

    @Override
    public void sendIceCandidate(String userId, String callId, String candidate) {
        CallSession session = activeCalls.get(callId);
        if (session != null && session.getStatus().equals("ACTIVE")) {
            String otherUser = session.getCaller().equals(userId) ? session.getCallee() : session.getCaller();
            IceCandidate iceCandidate = new IceCandidate(callId, userId, candidate);
            messageSender.sendToUser(otherUser, "/queue/ice", iceCandidate);
            log.debug("ICE candidate sent from {} to {}, callId: {}", userId, otherUser, callId);
        }
    }

    private String findCallId(String caller, String callee) {
        return activeCalls.values().stream()
                .filter(s -> s.getCaller().equals(caller) && s.getCallee().equals(callee) && s.getStatus().equals("RINGING"))
                .map(CallSession::getCallId)
                .findFirst()
                .orElse(null);
    }

    @Data
    private static class MediaOffer {
        private final String callId;
        private final String from;
        private final String sdp;
    }

    @Data
    private static class IceCandidate {
        private final String callId;
        private final String from;
        private final String candidate;
    }

    //==================================
    //      WebSocket消息发送功能
    //==================================

    /**
     * 向所有客户端发送字典更新事件
     *
     * @param dictCode 字典编码
     */
    @Override
    public void broadcastDictChange(String dictCode) {
        DictEvent event = new DictEvent(dictCode);
        sendDictEvent(event);
    }

    /**
     * 发送字典事件消息
     *
     * @param event 字典事件
     */
    private void sendDictEvent(DictEvent event) {
        try {
            messageSender.broadcast("/topic/dict", event);
            log.info("Sent dictionary event notification, dictCode: {}", event.getDictCode());
        } catch (Exception e) {
            log.error("Failed to send dictionary event", e);
        }
    }

    /**
     * 向特定用户发送系统消息
     * 
     * @param username 用户名
     * @param message 消息内容
     */
    @Override
    public void sendNotification(String username, Object message) {
        try {
            messageSender.sendToUser(username, "/queue/messages", message);
            log.info("Sent notification to user [{}]", username);
        } catch (Exception e) {
            log.error("Failed to send notification to user [{}]", username, e);
        }
    }
    
    /**
     * 发送广播消息给所有用户
     * 
     * @param message 消息内容
     */
    public void broadcastMessage(String message) {
        try {
            SystemMessage systemMessage = new SystemMessage("System", message, System.currentTimeMillis());
            messageSender.broadcast("/topic/public", systemMessage);
            log.info("Sent broadcast message: {}", message);
        } catch (Exception e) {
            log.error("Failed to send broadcast message", e);
        }
    }
    
    /**
     * 系统消息对象
     */
    @Data
    public static class CallRequest {
        private final String caller;
        private final long timestamp;
        private String callId;

        public CallRequest(String caller, long timestamp) {
            this.caller = caller;
            this.timestamp = timestamp;
            this.callId = "call-" + timestamp + "-" + caller.hashCode();
        }
    }

    @Data
    public static class CallResponse {
        private final String callId;
        private final String status; // "ACCEPTED", "REJECTED", "ENDED"
        private final String reason;
        private final long timestamp;

        public CallResponse(String callId, String status, String reason) {
            this.callId = callId;
            this.status = status;
            this.reason = reason;
            this.timestamp = System.currentTimeMillis();
        }
    }

    @Data
    public static class CallSession {
        private final String callId;
        private final String caller;
        private final String callee;
        private String status; // "RINGING", "ACTIVE", "ENDED"
        private long startTime;
        private long endTime;

        public CallSession(String callId, String caller, String callee) {
            this.callId = callId;
            this.caller = caller;
            this.callee = callee;
            this.status = "RINGING";
            this.startTime = System.currentTimeMillis();
        }
    }

    @Data
    public static class SystemMessage {
        private String sender;
        private String content;
        private long timestamp;
        
        public SystemMessage(String sender, String content, long timestamp) {
            this.sender = sender;
            this.content = content;
            this.timestamp = timestamp;
        }
    }
}
